#include "GeometryFileWriter.h"

namespace Pulse
{
	GeometryFileWriter::GeometryFileWriter( void )
	{
	}

	GeometryFileWriter::~GeometryFileWriter( void )
	{

	}

	void GeometryFileWriter::ResetData( void )
	{
		m_doc.ClearChildren();
		m_pVertexStream.Reset();
		m_pIndexStream.Reset();
		m_pRootNode.Reset();

		// Reset counters
		m_numPositions = 0;
		m_numNormals = 0;

		m_numTextureDiffuse = 0;

		m_numIndices = 0;
		m_b16BitIndices = TRUE;

		m_numSubMeshes = 0;
	}

	void GeometryFileWriter::StartWrite( const CHAR *pSaveFilename )
	{
		ResetData();

		m_filename = pSaveFilename;

		m_pRootNode = m_doc.CreateChild( PSX_String("Geometry") );
		{
			m_pInfo = m_pRootNode->CreateChild( PSX_String("Info") );

			// NOTE: Non of these nodes in the steams are linked to the root node.
			//	The nodes will be reevaluated if it needs to be linked 
			//	or not(i.e. Contains vertices? Indices? etc...).
			m_pVertexStream = m_pRootNode->CreateChild( GeometryElementInfo::GetVertexStreamName() );
			{
				m_pPositions = m_doc.CreateElement( GeometryElementInfo::GetVertexName( EVertexElement::POSITION ) );
				{
					m_pPositionsElements = m_doc.CreateElement( PSX_String("Elements") );
				}

				m_pNormals = m_doc.CreateElement( GeometryElementInfo::GetVertexName( EVertexElement::NORMAL  ) );
				{
					m_pNormalsElements = m_doc.CreateElement( PSX_String("Elements") );
				}

				m_pTextureDiffuse = m_doc.CreateElement( GeometryElementInfo::GetVertexName( EVertexElement::TEXTURE_DIFFUSE ) );
				{
					m_pTextureDiffuseElements = m_doc.CreateElement( PSX_String("Elements") );
				}
			}
			
			m_pIndexStream = m_pRootNode->CreateChild( GeometryElementInfo::GetIndexStreamName() );
			{
				m_pIndices = m_doc.CreateElement( GeometryElementInfo::GetIndexName() );
				{
					m_pIndicesElements = m_doc.CreateElement( PSX_String("Elements") );
				}
			}

			m_pSubGeometry = m_pRootNode->CreateChild( GeometryElementInfo::GetSubGeometryStreamName() );
			{
				m_pSubGeometryElements = m_doc.CreateElement( PSX_String("Elements") );
			}
		}
	}

	void GeometryFileWriter::EndWrite( void )
	{
		if ( m_filename.IsEmpty() )
			return;

		// Connect necessary streams if it exists
		PerformLinkEvaluation();

		m_doc.SaveFile( m_filename.GetBuffer() );
		m_filename.Clear();
	}

	SIZE_T32 GeometryFileWriter::AddPosition( FLOAT x, FLOAT y, FLOAT z )
	{
		String val;
		if ( m_numPositions )
			AddSpace( m_pPositionsElements );

		// Add X, Y, Z and a space between numbers
		val = String::ToString( x );
		m_pPositionsElements->AddValue( val.GetBuffer() );

		AddSpace( m_pPositionsElements );
		val = String::ToString( y );
		m_pPositionsElements->AddValue( val.GetBuffer() );
		
		AddSpace( m_pPositionsElements );
		val = String::ToString( z );
		m_pPositionsElements->AddValue( val.GetBuffer() );
		
		++m_numPositions;

		return m_numPositions;
	}

	SIZE_T32 GeometryFileWriter::AddPosition( Vector3 *pPos )
	{
		return AddPosition( pPos->x, pPos->y, pPos->z );
	}

	SIZE_T32 GeometryFileWriter::AddNormal( FLOAT x, FLOAT y, FLOAT z )
	{
		String val;
		if ( m_numNormals )
			AddSpace( m_pNormalsElements );

		// Add X, Y, Z and a space between numbers
		val = String::ToString( x );
		m_pNormalsElements->AddValue( val.GetBuffer() );

		AddSpace( m_pNormalsElements );
		val = String::ToString( y );
		m_pNormalsElements->AddValue( val.GetBuffer() );

		AddSpace( m_pNormalsElements );
		val = String::ToString( z );
		m_pNormalsElements->AddValue( val.GetBuffer() );

		++m_numNormals;

		return m_numNormals;
	}
	
	SIZE_T32 GeometryFileWriter::AddNormal( Vector3 *pNormal )
	{
		return AddNormal( pNormal->x, pNormal->y, pNormal->z );
	}

	SIZE_T32 GeometryFileWriter::AddTextureDiffuse( FLOAT x, FLOAT y )
	{
		String val;
		if ( m_numTextureDiffuse )
			AddSpace( m_pTextureDiffuseElements );

		// Add X, Y, Z and a space between numbers
		val = String::ToString( x );
		m_pTextureDiffuseElements->AddValue( val.GetBuffer() );

		AddSpace( m_pTextureDiffuseElements );
		val = String::ToString( y );
		m_pTextureDiffuseElements->AddValue( val.GetBuffer() );

		++m_numTextureDiffuse;

		return m_numTextureDiffuse;
	}
	
	SIZE_T32 GeometryFileWriter::AddTextureDiffuse( Vector2 *pUVDiffuse )
	{
		return AddTextureDiffuse( pUVDiffuse->x, pUVDiffuse->y );
	}

	SIZE_T32 GeometryFileWriter::AddIndex( UINT index )
	{
		// Check if we need to use 16 or 32 bit indices
		if ( index > PSX_USHORT_MAX )
			m_b16BitIndices = FALSE;

		if ( m_numIndices )
			AddSpace( m_pIndicesElements );

		String val = String::ToString( index );
		m_pIndicesElements->AddValue( val.GetBuffer() );
		++m_numIndices;
		return m_numIndices;
	}

	SIZE_T32 GeometryFileWriter::AddSubGeometry( UPOS_T32 indicesOffset, SIZE_T32 numTriangles )
	{
		XMLElementPtr pNewSubMesh = m_pSubGeometryElements->CreateChild( PSX_String("SubGeometry") );

		//pNewSubMesh->CreateChild(PSX_String("Topology"))->SetValue( PSX_String("TriangleList") );
		pNewSubMesh->CreateChild(PSX_String("IndexOffset"))->SetValue( String::ToString(indicesOffset).GetBuffer() );
		pNewSubMesh->CreateChild(PSX_String("NumPrimitives"))->SetValue( String::ToString(numTriangles).GetBuffer() );

		return ++m_numSubMeshes;
	}

	void GeometryFileWriter::AddSpace( XMLElement *pElem )
	{
		pElem->AddValue( PSX_String(" ") );
	}

	void GeometryFileWriter::PerformLinkEvaluation( void )
	{
		// NOTE: There's a bug that unattached nodes doesn't get delete. So we have to link it then manually delete.

		// Do we have any vertex streams available?
		if ( m_numPositions )
		{
			//m_pPositions->SetAttribute( PSX_String("Count"), m_numPositions );
			m_pPositions->CreateChild(PSX_String("Count"))->SetValue(String::ToString( m_numPositions ).GetBuffer());
			m_pPositions->AttachChild( m_pPositionsElements );
			m_pVertexStream->AttachChild( m_pPositions );
		}
		else
		{
			m_pPositions->AttachChild( m_pPositionsElements );
			m_pVertexStream->AttachChild( m_pPositions );
			m_pVertexStream->RemoveChild( GeometryElementInfo::GetVertexName( EVertexElement::POSITION ) );
		}

		if ( m_numNormals )
		{
			//m_pNormals->SetAttribute( PSX_String("Count"), m_numNormals );
			m_pNormals->CreateChild(PSX_String("Count"))->SetValue(String::ToString( m_numNormals ).GetBuffer());
			m_pNormals->AttachChild( m_pNormalsElements );
			m_pVertexStream->AttachChild( m_pNormals );
		}
		else
		{
			m_pNormals->AttachChild( m_pNormalsElements );
			m_pVertexStream->AttachChild( m_pNormals );
			m_pVertexStream->RemoveChild( GeometryElementInfo::GetVertexName( EVertexElement::NORMAL ) );
		}

		if ( m_numTextureDiffuse )
		{
			//m_pTextureDiffuse->SetAttribute( PSX_String("Count"), m_numTextureDiffuse );
			m_pTextureDiffuse->CreateChild(PSX_String("Count"))->SetValue(String::ToString( m_numTextureDiffuse ).GetBuffer());
			m_pTextureDiffuse->AttachChild( m_pTextureDiffuseElements );
			m_pVertexStream->AttachChild( m_pTextureDiffuse );
		}
		else
		{
			m_pTextureDiffuse->AttachChild( m_pTextureDiffuseElements );
			m_pVertexStream->AttachChild( m_pTextureDiffuse );
			m_pVertexStream->RemoveChild( GeometryElementInfo::GetVertexName( EVertexElement::TEXTURE_DIFFUSE ) );
		}

		// Do we have indices available?
		if ( m_numIndices )
		{
			//m_pIndices->SetAttribute( PSX_String("Count"), m_numIndices );
			//m_pIndices->SetAttribute( PSX_String("Use16Bit"), m_b16BitIndices );
			m_pIndices->CreateChild(PSX_String("Count"))->SetValue(String::ToString( m_numIndices ).GetBuffer());
			m_pIndices->CreateChild(PSX_String("Use16Bit"))->SetValue(String::ToString( m_b16BitIndices ).GetBuffer());
			m_pIndices->AttachChild( m_pIndicesElements );
			m_pIndexStream->AttachChild( m_pIndices );
		}
		else
		{
			m_pIndices->AttachChild( m_pIndicesElements );
			m_pIndexStream->AttachChild( m_pIndices );
			m_pIndexStream->ClearChildren();
		}

		// Link subgeometries
		if ( m_numSubMeshes )
		{
			m_pSubGeometry->CreateChild(PSX_String("Count"))->SetValue(String::ToString(m_numSubMeshes).GetBuffer());
			m_pSubGeometry->AttachChild(m_pSubGeometryElements);
		}
		else
		{
			m_pSubGeometry->AttachChild(m_pSubGeometryElements);
			m_pSubGeometry->ClearChildren();
		}
	}


}